/************************************************************
 * Copyright (C), 2009-2011, Tech. Co., Ltd.
 * FileName:		// 文件名
 * Author:			// 作者
 * Date:			// 日期
 * Description:		// 模块描述
 * Version:			// 版本信息
 * Function List:	// 主要函数及其功能
 *     1. -------
 * History:			// 历史修改记录
 *     <author>  <time>   <version >   <desc>
 *     xxxxx    16/10/12     1.0     build this moudle
 ***********************************************************/



#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>

#define COMPATILE_NAME "lt8619c"
/**
 &i2c0 {
      lt8619c: lt8619c@32 {
          compatible = "lt8619c";
          reg = <0x32>;
          reset-gpio = <&gpio4 23 GPIO_ACTIVE_HIGH>;
      };
**/


struct lt8619c_priv {
	struct i2c_client *client;
	int reset_gpio; 
};
static struct lt8619c_priv *lt8619c;
static const struct i2c_device_id lt8619c_id[] = {
	{ COMPATILE_NAME, 0 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, lt8619c_id);

#ifdef CONFIG_OF
static const struct of_device_id lt8619c_of_table[] = {
	{ .compatible = COMPATILE_NAME },
	{ }
};
MODULE_DEVICE_TABLE(of, lt8619c_of_table);
#endif

static int debug_enable = 0;
module_param(debug_enable,int, 0644);


/* I2C handy operation */

int i2c_master_reg8_send(const struct i2c_client *client, const char reg, const char *buf, int count)
{
	struct i2c_adapter *adap=client->adapter;
	struct i2c_msg msg;
	int ret;
	char *tx_buf = (char *)kmalloc(count + 1, GFP_KERNEL);
	if(!tx_buf)
		return -ENOMEM;
	tx_buf[0] = reg;
	memcpy(tx_buf+1, buf, count); 

	msg.addr = client->addr;
	msg.flags = client->flags;
	msg.len = count + 1;
	msg.buf = (char *)tx_buf;
//	msg.scl_rate = scl_rate;
//	msg.udelay = client->udelay;

	ret = i2c_transfer(adap, &msg, 1);
	kfree(tx_buf);
	return (ret == 1) ? count : ret;

}


int i2c_master_reg8_recv(const struct i2c_client *client, const char reg, char *buf, int count)
{
	struct i2c_adapter *adap=client->adapter;
	struct i2c_msg msgs[2];
	int ret;
	char reg_buf = reg;
	
	msgs[0].addr = client->addr;
	msgs[0].flags = client->flags;
	msgs[0].len = 1;
	msgs[0].buf = &reg_buf;
//	msgs[0].scl_rate = scl_rate;
//	msgs[0].udelay = client->udelay;

	msgs[1].addr = client->addr;
	msgs[1].flags = client->flags | I2C_M_RD;
	msgs[1].len = count;
	msgs[1].buf = (char *)buf;
//	msgs[1].scl_rate = scl_rate;
//	msgs[1].udelay = client->udelay;

	ret = i2c_transfer(adap, msgs, 2);

	return (ret == 2)? count : ret;
}

/***************************************************

   LT8619C HDMI 转 LVDS 寄存器设置

***************************************************/

u8 I2CADR = 0x64;     // IIC address // bit0 是读写标志位；
//u8 I2CADR = 0x32;// IIC address // bit7 是读写标志位；

u16 H_FrontP   = 0x0000;
u16 H_BackP	   = 0x0000;
u16 H_SyncWid  = 0x0000;

u16 H_total	   = 0x0000;
u16 V_total	   = 0x0000;

u16 H_Active   = 0x0000;
u16 V_Active   = 0x0000;

u8	V_FrontP   = 0x00;
u8	V_BackP	   = 0x00;
u8	V_SyncWid  = 0x00;

u32 HDMI_CLK_Cnt = 0x00000000;

//**********************************************//




static int LT8619C_I2C_Write_Byte( u8 RegAddr, u8 data )                   // IIC 写操作
{

	return i2c_master_reg8_send(lt8619c->client, RegAddr, &data, 1);
}
extern u8 LT8619C_I2C_Read_Byte( u8 RegAddr )                          // IIC 读操作
{
	u8 val;
	i2c_master_reg8_recv(lt8619c->client, RegAddr, &val, 1);
	return val;
}

extern int LT8619C_I2C_Write_ByteN( u8 RegAddr, const u8 *data, u16 Num )   // 往寄存器写Num 个 byte的数值
{
	return i2c_master_reg8_send(lt8619c->client, RegAddr, data, Num);
}

static void Delay_ms(int ms)
{
	msleep(ms);
}


//*********************LVDS Output Config************************//

#define _1_Port_LVDS_	0x00
#define _2_Port_LVDS_	0x01

#define _DE_Mode_	0x20
#define _Sync_Mode_ 0x00            // LT8619C 的 sync mode 包含有 DE 信号

#define _8_bit_ColorDepth_	0x00
#define _6_bit_ColorDepth_	0x10

#define _C_D_Port_Swap_En	0x02    // if enable, 1 port output select port-D,2 ports output select port-D+port-C
#define _C_D_Port_Swap_Dis	0x00    // if disable,1 port output select port-C,2 ports output select port-C+port-D

#define _R_B_Color_Swap_En	0x01    // LVDS RGB data R/B Swap
#define _R_B_Color_Swap_Dis 0x00    // normal

#define _LVDS_Output_En		0x40
#define _LVDS_Output_Dis	0x00

#define _VESA_	0x00
#define _JEIDA_ 0x80

//================================//

//	如果有接外部参考电阻(Pin 16 - REXT, 2K 电阻)，优先使用外部参考电阻
#define _Internal_	0x88    // internal resistance
#define _External_	0x80    // external resistance(Pin 16 - REXT, 2K resistance)

//================================//

// 设置IIS 音频输出，IIS和SPDIF只能二选一
#define _IIS_Output_ 0x11

// 设置SPDIF 音频输出，IIS和SPDIF只能二选一
#define _SPDIF_Output_ 0x39

//**********************************************//
//xxPC:full range;	xxTV:limit range
#define SDTV	0x00
#define SDPC	0x10
#define HDTV	0x20
#define HDPC	0x30

//**********************************************//

#define _6bit_Dither_En		0x38
#define _6bit_Dither_Dis	0x00

//**********************************************//

typedef  enum
{
	LOW	   = 0,
	HIGH   = !LOW
}
Pin_Status;

//================================//

//u8 LT8619C_Output_Mode ;// = _YUV_Output_;
u8		Refer_Resistance;   //= _Internal_;

bool	Load_HDCPKey_En;    //= 0;//  1:外接 HDCP key; 0:不使用 HDCP Key

u8		CP_Convert_Mode;    //= HDPC;

// when LT8619C_Output_Mode == _LVDS_Output_
u8		LVDS_Port;
u8		LVDS_SyncMode;
u8		LVDS_ColorDeepth;

u8		LVDS_C_D_Port_Swap;
u8		LVDS_R_B_Color_Swap;

u8		LVDS_Map;

u8		LVDS_Output_En;

u8		Audio_Output_Mode;  // = _IIS_Output_;
bool	Audio_Output_En;    // = 1;// 1 : enable Audio

//**********************************************//
void LT8619C_Config( void )
{
//	LT8619C_Output_Mode = _LVDS_Output_ ;

//	内部 / 外部参考电阻(Pin 16 - REXT, 2K 电阻) 选择
	Refer_Resistance = _Internal_;              // _Internal_	0x88 // internal resistance
	// _External_	0x80 // external resistance(Pin 16 - REXT, 2K resistance)

	Load_HDCPKey_En = 0;                        //  1:外接 HDCP key; 0:不使用 HDCP Key

	//===============Full/Limit range=================//
	CP_Convert_Mode = HDPC;
	//================================//

	Audio_Output_Mode = _IIS_Output_;           // _IIS_Output_
	//--------------------------------------// _SPDIF_Output_

	Audio_Output_En = 1;                        // 1 : enable Audio Output

	//******************************************//

	// when LT8619C_Output_Mode == _LVDS_Output_
	LVDS_Port = _2_Port_LVDS_;                  // _1_Port_LVDS_
	// _2_Port_LVDS_

	LVDS_SyncMode = _Sync_Mode_;                // _Sync_Mode_
	// _DE_Mode_

	LVDS_ColorDeepth = _8_bit_ColorDepth_;      // _8_bit_ColorDepth_
	// _6_bit_ColorDepth_

	LVDS_C_D_Port_Swap = _C_D_Port_Swap_Dis;    // _C_D_Port_Swap_En : 1 port output select port-D,2 ports output select port-D+port-C
	// _C_D_Port_Swap_Dis : 1 port output select port-C,2 ports output select port-C+port-D

	LVDS_R_B_Color_Swap = _R_B_Color_Swap_Dis;  // _R_B_Color_Swap_En: LVDS RGB data R/B Swap
	// _R_B_Color_Swap_Dis: Normal

	LVDS_Map = _VESA_;                          // _VESA_
	// _JEIDA_

	LVDS_Output_En = _LVDS_Output_En;           // enable LVDS output

	//******************************************//
}

//**********************************************//

// 读LT8619C的ID，可用于测试IIC通讯是否正常
void Read_LT8619C_ID( void )
{
	I2CADR = 0x64;                                                                  // IIC 地址是0x64
	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );                                           // register bank；先往0xFF 寄存器写0x60，切换到0x60这个bank

#ifdef _DEBUG_MODE_
	//	Debug_Printf( "\r\nRead LT8619C ID " );
	Debug_Printf( "\r\n " );
	Debug_DispStrNum( "\r\nLT8619C Chip ID = ", LT8619C_I2C_Read_Byte( 0x00 ) );    // 0x16
	Debug_DispStrNum( " ", LT8619C_I2C_Read_Byte( 0x01 ) );                         // 0x04
	Debug_Printf( "\r\n " );
#endif
}

//**********************Timing**************************//

// 通过读LT8619C的状态寄存器，可以知道前端HDMI 信号的Timing：
// video check
void HDMI_Timing_Read( void )
{
	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );   //register bank

	LT8619C_I2C_Write_Byte( 0x0c, 0xFB );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x0c, 0xFF );   // reset video check
	Delay_ms( 100 );

	H_FrontP   = LT8619C_I2C_Read_Byte( 0x1a ) * 0x100 + LT8619C_I2C_Read_Byte( 0x1b );
	H_SyncWid  = LT8619C_I2C_Read_Byte( 0x14 ) * 0x100 + LT8619C_I2C_Read_Byte( 0x15 );
	H_BackP	   = LT8619C_I2C_Read_Byte( 0x18 ) * 0x100 + LT8619C_I2C_Read_Byte( 0x19 );

	H_Active = LT8619C_I2C_Read_Byte( 0x22 ) * 0x100 + LT8619C_I2C_Read_Byte( 0x23 );

	H_total = LT8619C_I2C_Read_Byte( 0x1E ) * 0x100 + LT8619C_I2C_Read_Byte( 0x1F );

	V_FrontP   = LT8619C_I2C_Read_Byte( 0x17 );
	V_SyncWid  = LT8619C_I2C_Read_Byte( 0x13 );
	V_BackP	   = LT8619C_I2C_Read_Byte( 0x16 );

	V_Active = LT8619C_I2C_Read_Byte( 0x20 ) * 0x100 + LT8619C_I2C_Read_Byte( 0x21 );

	V_total = ( LT8619C_I2C_Read_Byte( 0x1C ) & 0x0F ) * 0x100 + LT8619C_I2C_Read_Byte( 0x1D );

	LT8619C_I2C_Write_Byte( 0xFF, 0x80 ); //register bank

	HDMI_CLK_Cnt = ( LT8619C_I2C_Read_Byte( 0x44 ) & 0x07 ) * 0x10000 + LT8619C_I2C_Read_Byte( 0x45 ) * 0x100 + LT8619C_I2C_Read_Byte( 0x46 );
	// HDMI_CLK = HDMI_CLK_Cnt * 1000 ;
}

//**********************************************//

void LT8619C_ColorConfig( void )
{
	u8 Temp_CSC = 0x00;

	LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
	Temp_CSC = LT8619C_I2C_Read_Byte( 0x71 );

	if( ( ( Temp_CSC & 0x60 ) == 0x20 ) || ( ( Temp_CSC & 0x60 ) == 0x40 ) )    // 如果HDMI的色彩空间不是RGB，需要进行色彩空间转换。
	{
		LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
		LT8619C_I2C_Write_Byte( 0x07, 0x8C );                                   // YCbCr to RGB ClK enable

		if( ( Temp_CSC & 0x60 ) == 0x20 )
		{
			LT8619C_I2C_Write_Byte( 0x52, 0x01 );                               // YUV422 to YUV444 enable

#ifdef _DEBUG_MODE_
			Debug_Printf( "\r\nHDMI Color Space is	YUV422");
#endif
		}else
		{
			LT8619C_I2C_Write_Byte( 0x52, 0x00 );                               // YUV444

#ifdef _DEBUG_MODE_
			Debug_Printf( "\r\nHDMI Color Space is	YUV444");
#endif
		}

		LT8619C_I2C_Write_Byte( 0x53, 0x40 + CP_Convert_Mode );                 // 0x40:YUV to RGB enable;
	}else
	{
		LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
		LT8619C_I2C_Write_Byte( 0x07, 0x80 );
		LT8619C_I2C_Write_Byte( 0x52, 0x00 );
		LT8619C_I2C_Write_Byte( 0x53, 0x00 );                                   // 0x00: ColorSpace bypass

#ifdef _DEBUG_MODE_
		Debug_Printf( "\r\nHDMI Color Space is	RGB");
#endif
	}
}

//*****************************EDID write*************************************//

const u8 ONCHIP_EDID[256] = {
/* 1280x720P 60Hz

   0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x32, 0x8d, 0xc5, 0x07, 0x01, 0x00, 0x00, 0x00,
   0x2e, 0x14, 0x01, 0x03, 0x80, 0x59, 0x32, 0x78, 0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
   0x0f, 0x50, 0x54, 0x21, 0x08, 0x80, 0x71, 0x40, 0x81, 0x00, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00,
   0xa9, 0xc0, 0xa9, 0x40, 0xd1, 0xc0, 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28,
   0x55, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00, 0x1b, 0x30,
   0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
   0x4b, 0x1a, 0x51, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
   0x00, 0x4c, 0x4f, 0x4e, 0x54, 0x49, 0x55, 0x4d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x57,

   0x02, 0x03, 0x2f, 0xc1, 0x48, 0x84, 0x13, 0x03, 0x12, 0x02, 0x11, 0x10, 0x1f, 0x23, 0x09, 0x07,
   0x07, 0x83, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0f, 0xe3, 0x05, 0x03, 0x01, 0x72, 0x03, 0x0c, 0x00,
   0x40, 0x00, 0x80, 0x1e, 0x20, 0xd0, 0x08, 0x01, 0x40, 0x07, 0x3f, 0x40, 0x50, 0x90, 0xa0, 0x01,
   0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28, 0x55, 0x40, 0xc4, 0x8e, 0x21, 0x00, 0x00,
   0x1e, 0x66, 0x21, 0x56, 0xaa, 0x51, 0x00, 0x1e, 0x30, 0x46, 0x8f, 0x33, 0x00, 0xc4, 0x8e, 0x21,
   0x00, 0x00, 0x1e, 0x20, 0x1c, 0x56, 0x86, 0x50, 0x00, 0x20, 0x30, 0x0e, 0x38, 0x13, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x1e, 0x0e, 0x1f, 0x00, 0x80, 0x51, 0x00, 0x1e, 0x30, 0x40, 0x80, 0x37,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef
   //*/

// 1080P60
	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x32, 0x8d, 0x19, 0x86, 0x01, 0x00, 0x00, 0x00,
	0x18, 0x19, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78, 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27,
	0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
	0x45, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
	0x46, 0x1e, 0x50, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
	0x00, 0x4c, 0x4f, 0x4e, 0x54, 0x49, 0x55, 0x4d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x18,

	0x02, 0x03, 0x22, 0x70, 0x23, 0x09, 0x7f, 0x07, 0x83, 0x01, 0x00, 0x00, 0x41, 0x90, 0x66, 0x03,
	0x0c, 0x00, 0x10, 0x00, 0xb8, 0x10, 0x08, 0x60, 0x22, 0x00, 0x12, 0x8e, 0x21, 0x08, 0x08, 0x18,
	0x8c, 0x0a, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc4, 0x8e,
	0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8
};


/***********************************************************

***********************************************************/
//8619C 的EDID 写入，可以操作256次 写0x90寄存器地址，再写1 byte EDID数据；
// 或者写一次0x90地址，连续写256 次1 byte EDID数据。
// 写EDID的时候，寄存器地址不要递增。
void Write_EDID2HDMI_Shadow( void )
{
	LT8619C_I2C_Write_Byte( 0x8f, 0x00 );
	LT8619C_I2C_Write_ByteN( 0x90, &ONCHIP_EDID[0], 256 ); // 往0x90寄存器连续写256个EDID 值。
}

//********************HPD Contorl************************//

void LT8619C_SetHPD( bool level )
{
	LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
	if( level )
	{
		LT8619C_I2C_Write_Byte( 0x06, LT8619C_I2C_Read_Byte( 0x06 ) | 0x08 );
	}else
	{
		LT8619C_I2C_Write_Byte( 0x06, LT8619C_I2C_Read_Byte( 0x06 ) & 0xf7 );
	}
}

//**********************Load HDCP key******************************//

int LT8619C_LoadHDCPKey( void )
{
	u8 flag_load_key_done = 0, loop_cnt = 0;

	LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
	LT8619C_I2C_Write_Byte( 0xb2, 0x50 );
	LT8619C_I2C_Write_Byte( 0xa3, 0x77 );
	while( loop_cnt <= 5 ) //wait load hdcp key done.
	{
		loop_cnt++;
		Delay_ms( 50 );
		flag_load_key_done = LT8619C_I2C_Read_Byte( 0xc0 ) & 0x08;
		if( flag_load_key_done )
		{
			break;
		}
	}
	LT8619C_I2C_Write_Byte( 0xb2, 0xd0 );
	LT8619C_I2C_Write_Byte( 0xa3, 0x57 );
	if( flag_load_key_done )
	{
		//    Debug_Printf("\r\nLoad KEY done^_^");
		return 1;
	}else
	{
		//    Debug_Printf("\r\nLoad KEY error#####");
		return 0;
	}
}

/*********************Audio config*******************************/

// LT8619C 上电默认是IIS 输出，如果只需要IIS输出，可以不要设置Audio寄存器
void LT8619C_AudioConfig( void )
{
	if( Audio_Output_En )
	{
		if( Audio_Output_Mode == _IIS_Output_ )
		{
			LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
			// SD0 channel selected
			LT8619C_I2C_Write_Byte( 0x07, 0x16 ); // 0x16: Left justified(I2S compatible); default
			// 0x14: Right justified;
			// If the setting is not correct, there will be noise.

			LT8619C_I2C_Write_Byte( 0x08, 0x0F );

			LT8619C_I2C_Write_Byte( 0x5d, 0xc9 );

			LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
			LT8619C_I2C_Write_Byte( 0x4c, 0x00 ); // Setting 19 pin as IIS SD0
		}else // SPDIF output
		{
			LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
			LT8619C_I2C_Write_Byte( 0x08, 0x8F );

			LT8619C_I2C_Write_Byte( 0x5d, 0xc9 );

			LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
			LT8619C_I2C_Write_Byte( 0x4c, 0xbf ); // Setting 19 pin SPDIF
		}
	}else // Close Audio output
	{
		LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
		LT8619C_I2C_Write_Byte( 0x4c, 0xff );
		LT8619C_I2C_Write_Byte( 0x4d, 0xff );
	}
}

/***********************************************************

***********************************************************/
void LVDSPLL_Lock_Det( void )
{
	u8 temp = 0;

	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0x0e, 0xfd );
	Delay_ms( 5 );
	LT8619C_I2C_Write_Byte( 0x0e, 0xff );

	Delay_ms( 100 );

	LT8619C_I2C_Write_Byte( 0xFF, 0x80 );

	while( ( LT8619C_I2C_Read_Byte( 0x87 ) & 0x20 ) == 0x00 )
	{
		LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
		LT8619C_I2C_Write_Byte( 0x0e, 0xfd );
		Delay_ms( 5 );
		LT8619C_I2C_Write_Byte( 0x0e, 0xff );
		Delay_ms( 100 );

		LT8619C_I2C_Write_Byte( 0xFF, 0x80 );

		temp++;
		if( temp > 3 )
		{
			break;
		}
	}
}

/***********************************************************/

void LT8619C_RX_reset( void )
{
	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0x0e, 0xbf );
	LT8619C_I2C_Write_Byte( 0x09, 0xfd );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x0e, 0xff );
	LT8619C_I2C_Write_Byte( 0x09, 0xff );

	LT8619C_I2C_Write_Byte( 0x0e, 0xc7 );
	LT8619C_I2C_Write_Byte( 0x09, 0x0f );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x0e, 0xff );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x09, 0x8f );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x09, 0xff );
	Delay_ms( 50 );
}

/***********************************************************

***********************************************************/
void Wait_HDMI_stable( void )
{
	LT8619C_I2C_Write_Byte( 0xFF, 0x80 );

	while( ( LT8619C_I2C_Read_Byte( 0x44 ) & 0x08 ) == 0x00 ) // 等待RX HDMI CLK信号
	{
		Delay_ms( 100 );
	}

	Delay_ms( 100 );
//	Delay_ms( 200 );
//	Delay_ms( 200 );

	while( ( LT8619C_I2C_Read_Byte( 0x44 ) & 0x08 ) == 0x00 ) // 等待RX HDMI CLK信号
	{
		Delay_ms( 100 );
	}

	Delay_ms( 100 );
//	Delay_ms( 200 );

	while( ( LT8619C_I2C_Read_Byte( 0x87 ) & 0x10 ) == 0x00 ) // 等待 RX PLL locked
	{
#ifdef _DEBUG_MODE_
		Debug_Printf( "\r\nwait RXPLL lock" );
#endif
		LT8619C_RX_reset( );

		LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
		LT8619C_I2C_Write_Byte( 0x0e, 0xfd );
		LT8619C_I2C_Write_Byte( 0x0e, 0xff );
		Delay_ms( 100 );

		LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
	}

	Delay_ms( 100 );

	while( ( LT8619C_I2C_Read_Byte( 0x13 ) & 0x01 ) == 0x00 ) // 等待H SYNC 稳定
	{
		Delay_ms( 100 );
	}

	Delay_ms( 100 );
//	Delay_ms( 200 );
/*
	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0x09, 0x7f );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x09, 0xFF ); // reset HDMI RX logic
	Delay_ms( 100 );
//*/
	HDMI_Timing_Read( );
}

/***********************************************************

***********************************************************/
int LvdsData_check( void )
{
//	u8	read_data1, read_data2;
	u8	i, LVDScheck_bit0;
	u8	x = 0;

	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );

	for( i = 0; i < 200; i++ )
	{
		LVDScheck_bit0 = LT8619C_I2C_Read_Byte( 0x92 ) & 0x01;

		if( LVDScheck_bit0 == 0 )
		{
			x++;
		}
		Delay_ms( 2 );
	}

	if( ( x == 200 ) || ( x == 0 ) ){
		return 0;
	}else{
		return 1;
	}
}

/***********************************************************

***********************************************************/
void LT8619C_LVDSData_Det( void )
{
//	u8	read_data; //, read_data1, read_data2;
	u8 n = 0;

//	do
	while( !LvdsData_check( ) )
	{
		;
	}
	{
		//	if( !LvdsData_check( ) )
		{
			LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
			//	read_data = LT8619C_I2C_Read_Byte( 0x0D );
			//	LT8619C_I2C_Write_Byte( 0x0D, read_data & 0xf8 ); //reset LVDS/BT fifo
			LT8619C_I2C_Write_Byte( 0x0D, 0xf8 ); //reset LVDS/BT fifo
			Delay_ms( 10 );
			LT8619C_I2C_Write_Byte( 0x0D, 0xfe );
			LT8619C_I2C_Write_Byte( 0x0D, 0xff );

			n++;
		}

//		if( n > 4 )
//		{
//			//	memset(pLT8619C_RXStatus, 0, sizeof(_LT8619C_RXStatus));
//			break;
//		}
	}
//	while( !LvdsData_check( ) );
}

/***********************************************************/

void LT8619C_turn_off_LVDS( void )
{
	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0xba, 0x44 );             // bit2= 1 => turn off LVDS C
	LT8619C_I2C_Write_Byte( 0xc0, 0x44 );               // bit2= 1 => turn off LVDS D
}

/***********************************************************/

void LT8619C_turn_on_LVDS( void )
{
	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );

	if( LVDS_Port == _1_Port_LVDS_ )
	{
		if( LVDS_C_D_Port_Swap == _C_D_Port_Swap_Dis )
		{
			LT8619C_I2C_Write_Byte( 0xba, 0x18 );   // bit2= 0 => turn on LVDS C
			LT8619C_I2C_Write_Byte( 0xc0, 0x44 );   // turn off LVDS D
		}else
		{
			LT8619C_I2C_Write_Byte( 0xba, 0x44 );   // turn off LVDS C
			LT8619C_I2C_Write_Byte( 0xc0, 0x18 );   // bit2= 0 => turn on LVDS D
		}
	}else
	{
		LT8619C_I2C_Write_Byte( 0xba, 0x18 );       // bit2= 0 => turn on LVDS C
		LT8619C_I2C_Write_Byte( 0xc0, 0x18 );       // bit2= 0 => turn on LVDS D
	}
}

/***********************************************************

***********************************************************/
void LT8619C_Reset( void )
{
//	GPIO_WriteLow( GPIOC, GPIO_PIN_3 );             // Low
	gpio_direction_output(lt8619c->reset_gpio, 0);
	Delay_ms( 100 );
//	GPIO_WriteHigh( GPIOC, GPIO_PIN_3 );            // High
	gpio_direction_output(lt8619c->reset_gpio, 1);
	Delay_ms( 100 );
}

/***********************************************************/

void LT8619C_LVDS_Config( void )
{
//===============================//

	Wait_HDMI_stable( );

//===============================//
	// When HDMI signal is stable, LVDS PLL lock status needs to be detected. If it is not locked, LVDS PLL needs to be reset
	LVDSPLL_Lock_Det( );

//===============================//

	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0x0d, 0xfb );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x0d, 0xff );

//	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );

	LT8619C_I2C_Write_Byte( 0x59, LVDS_Map + LVDS_Output_En + LVDS_SyncMode + LVDS_ColorDeepth + LVDS_C_D_Port_Swap + LVDS_R_B_Color_Swap );

	if( LVDS_ColorDeepth == _8_bit_ColorDepth_ )
	{
		LT8619C_I2C_Write_Byte( 0x5f, _6bit_Dither_Dis );
	}else
	{
		LT8619C_I2C_Write_Byte( 0x5f, _6bit_Dither_En );
	}

//	LT8619C_I2C_Write_Byte( 0xa0, 0x58 );

	LT8619C_I2C_Write_Byte( 0xa4, LVDS_Port ); // 00 : CLK/1 ; 01: CLK/2

	LT8619C_I2C_Write_Byte( 0xa8, 0x00 );

	LT8619C_turn_off_LVDS( );
//-----------------------------------------//
//LVDS channel output current select


/*
   4'b0000 = 2.0mA;
   4'b0001 = 2.25mA;
   4'b0010 = 2.50mA;
   4'b0100 = 3.0mA;
   4'b1000 = 4.0mA;
   4'b1111 = 5.75mA;
   Step = 0.25mA;
   //*/
	LT8619C_I2C_Write_Byte( 0xb0, 0x66 );               //[7:4]:LVDS C channel d0 lane;[3:0] LVDS C channel d1 lane
	LT8619C_I2C_Write_Byte( 0xb1, 0x66 );               //[7:4]:LVDS C channel d2 lane;[3:0] LVDS C channel clk lane
	LT8619C_I2C_Write_Byte( 0xb2, 0x66 );               //[7:4]:LVDS C channel d3 lane;[3:0] LVDS D channel d0 lane
	LT8619C_I2C_Write_Byte( 0xb3, 0x66 );               //[7:4]:LVDS D channel d1 lane;[3:0] LVDS D channel d2 lane
	LT8619C_I2C_Write_Byte( 0xb4, 0x66 );               //[7:4]:LVDS D channel clk lane;[3:0] LVDS D channel d3 lane
//-----------------------------------------//

	LT8619C_I2C_Write_Byte( 0xb5, 0x41 );               // DC0 ; bit1 = 1 => DC0 PN swap
	LT8619C_I2C_Write_Byte( 0xb6, 0x41 );               // DC1 ; bit1 = 1 => DC1 PN swap
	LT8619C_I2C_Write_Byte( 0xb7, 0x41 );               // DC2 ; bit1 = 1 => DC2 PN swap
	LT8619C_I2C_Write_Byte( 0xb8, 0x4c );               // DCC ; bit7 = 1 => DCC PN swap
	LT8619C_I2C_Write_Byte( 0xb9, 0x41 );               // DC3 ; bit1 = 1 => DC3 PN swap

	LT8619C_I2C_Write_Byte( 0xbb, 0x41 );               // DD0 ; bit1 = 1 => DD0 PN swap
	LT8619C_I2C_Write_Byte( 0xbc, 0x41 );               // DD1 ; bit1 = 1 => DD1 PN swap
	LT8619C_I2C_Write_Byte( 0xbd, 0x41 );               // DD2 ; bit1 = 1 => DD2 PN swap
	LT8619C_I2C_Write_Byte( 0xbe, 0x4c );               // DDC ; bit7 = 1 => DDC PN swap
	LT8619C_I2C_Write_Byte( 0xbf, 0x41 );               // DD3 ; bit1 = 1 => DD3 PN swap

	LT8619C_I2C_Write_Byte( 0xa0, 0x18 );               // 0x18
	LT8619C_I2C_Write_Byte( 0xa1, 0xb0 );
	LT8619C_I2C_Write_Byte( 0xa2, 0x10 );

	LT8619C_I2C_Write_Byte( 0x5c, LVDS_Port );

	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0x06, 0x7f );

	LT8619C_I2C_Write_Byte( 0x0e, 0xfe );
	LT8619C_I2C_Write_Byte( 0x0d, 0xfb );
	Delay_ms( 10 );
	LT8619C_I2C_Write_Byte( 0x0d, 0xff );

	LT8619C_I2C_Write_Byte( 0x06, 0xff );
	LT8619C_I2C_Write_Byte( 0x0e, 0xff );   // LVDS PLL reset

	//如果在HDMI信号还没出现的时候，就调用色彩空间转换设置函数LT8619C_ColorConfig（）就有可能会判断错误，
	//因为这个函数是根据前端HDMI信号色彩空间的状态来设置。
	//如果HDMI 是YUV420输入，那只能直接按照420送出去,LT8619C没办法做色彩转换。

	LT8619C_ColorConfig( );                 // 如果HDMI的色彩空间是YUV422/YUV444，需要进行色彩空间转换。

	LT8619C_AudioConfig( );

	if( LVDS_Port == _2_Port_LVDS_ )
	{
		LT8619C_LVDSData_Det( );
	}

	LT8619C_turn_on_LVDS( );
}

//****************************Initital LT8619C******************************************/

void lt8619c_chip_init( void )
{
	I2CADR = 0x64; // IIC address // bit0 是读写标志位；
	//I2CADR = 0x32;// IIC address // bit7 是读写标志位；

//------------------------------------//

// 复位LT8619C，如果复位信号有接到前端主控GPIO控制的话。先拉低100ms，再拉高，延时50ms
	LT8619C_Reset( );

//------------------------------------//

	LT8619C_Config( );

//------------------------------------//

	Read_LT8619C_ID( ); // read chip ID

//------------------------------------//

// 如果HDMI是强制输出，可以不需要写EDID。

	LT8619C_SetHPD( LOW );

	Delay_ms( 10 );

	LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
	LT8619C_I2C_Write_Byte( 0x8E, 0x07 ); // Enable EDID shadow operation
	Write_EDID2HDMI_Shadow( );
	LT8619C_I2C_Write_Byte( 0x8E, 0x02 );

//-----------------------------------//

	if( Load_HDCPKey_En )
	{
		LT8619C_LoadHDCPKey( );
	}

	LT8619C_SetHPD( HIGH );

//-----------------------------------//
	LT8619C_I2C_Write_Byte( 0xFF, 0x80 );
	LT8619C_I2C_Write_Byte( 0x2c, 0x35 );   //RGD_CLK_STABLE_OPT[1:0]

	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0x04, 0xf2 );   // LVDS PLL lock detect module clock enable

	LT8619C_I2C_Write_Byte( 0x80, 0x08 );

//	使用外部参考电阻的时候，Pin16 管脚上只接2K 电阻，不能接Vcc1.8以及接地电容。
//	默认使用内部参考电阻
	LT8619C_I2C_Write_Byte( 0x89, Refer_Resistance );

	LT8619C_I2C_Write_Byte( 0x8b, 0x90 ); // Normal AFE

//	LT8619C_I2C_Write_Byte( 0xFF, 0x60 );
	LT8619C_I2C_Write_Byte( 0x06, 0xe7 );

	LT8619C_I2C_Write_Byte( 0xa0, 0x58 );

	LT8619C_LVDS_Config( );
}

/************************************** The End Of File **************************************/

static int lt8619c_probe(struct i2c_client *client,
				   const struct i2c_device_id *id)
{
	struct device_node *np;
	struct lt8619c_priv *chip;
	int gpio = -1;
	dev_info(&client->dev, "%s enter\n", __func__);
	np = client->dev.of_node;
	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (chip == NULL)
		return -ENOMEM;
	chip->client = client;
	i2c_set_clientdata(client, chip);
	if((gpio=of_get_named_gpio_flags(np, "reset-gpio", 0, NULL))<0){
		dev_err(&client->dev, "parse reset-gpio fail");
	}
	chip->reset_gpio = gpio; 
	lt8619c_chip_init();
	return 0;

}

static int lt8619c_remove(struct i2c_client *client)
{
//	int ret = 0;
	struct lt8619c_priv *chip = i2c_get_clientdata(client);
	(void)chip;
	dev_info(&client->dev, "%s enter\n", __func__);
	return 0;
}

void lt8619c_shutdown(struct i2c_client *client)
{
//	int ret = 0;
	struct lt8619c_priv *chip = i2c_get_clientdata(client);
	(void)chip;
	dev_info(&client->dev, "%s enter\n", __func__);
}

static struct i2c_driver lt8619c_driver = {
	.driver = {
		.name		= "lt8619c",
		.owner		= THIS_MODULE,
		.of_match_table	= of_match_ptr(lt8619c_of_table),
	},
	.probe		= lt8619c_probe,
	.remove		= lt8619c_remove,
	.id_table	= lt8619c_id,
	.shutdown = lt8619c_shutdown,
};

static int __init lt8619c_init(void)
{
	return i2c_add_driver(&lt8619c_driver);
}
/* register after i2c postcore initcall and before
 * subsys initcalls that may rely on these GPIOs
 */
#if 1
subsys_initcall_sync(lt8619c_init);
#else
module_init(lt8619c_init);
#endif
static void __exit lt8619c_exit(void)
{
	i2c_del_driver(&lt8619c_driver);
}
module_exit(lt8619c_exit);

MODULE_AUTHOR("tj <tj_wust@163.com>");
MODULE_DESCRIPTION("i2c demo driver");
MODULE_LICENSE("GPL");





